package com.macro.mall.utils;

import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName CustomCellWriteHandler
 * @Description 设置自适应列宽
 * @author zhaojianlin
 * @Date 2020年10月13日 下午2:03:18
 * @version 1.0.0
 */
public class CustomCellWriteHandler extends AbstractColumnWidthStyleStrategy {

    private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>();

    @SuppressWarnings("rawtypes")
    @Override
    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {
        boolean needSetWidth = isHead || !cellDataList.isEmpty();
        if (isHead && cell.getStringCellValue() != null && cell.getStringCellValue().contains("*")) {
            cell.setCellStyle(PoiUtils.getColumnFontStyle(writeSheetHolder.getSheet().getWorkbook(), IndexedColors.RED));
        }
        if (needSetWidth) {
            Map<Integer, Integer> maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo());
            if (maxColumnWidthMap == null) {
                maxColumnWidthMap = new HashMap<>();
                CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
            }

            Integer columnWidth = this.dataLength(cellDataList, cell, isHead);

            if (isHead) {
                columnWidth = this.getColumnWidthPushBefore(writeSheetHolder, cellDataList, cell, columnWidth, maxColumnWidthMap);
            }

            this.setColumnWidth(writeSheetHolder, columnWidth, cell, maxColumnWidthMap, false);
        }
    }

    private void setColumnWidth(WriteSheetHolder writeSheetHolder, Integer columnWidth, Cell cell, Map<Integer, Integer> maxColumnWidthMap, boolean isExcludeMax) {
        if (columnWidth >= 0) {
            if (columnWidth < 18) {
                columnWidth = 18;
            }
            if (columnWidth > 255) {
                columnWidth = 255;
            }

            Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
            if (isExcludeMax || maxColumnWidth == null || columnWidth > maxColumnWidth) {
                maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
                writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
            }
        }
    }

    @SuppressWarnings("rawtypes")
    private Integer getColumnWidthPushBefore(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell,
            Integer columnWidth, Map<Integer, Integer> maxColumnWidthMap) {
        Row row = writeSheetHolder.getSheet().getRow(cell.getRowIndex());
        Integer divisor = 1;
        String cellValue = cell.getStringCellValue();
        Integer columnIndex = cell.getColumnIndex();
        if (columnIndex == 0) {
            return columnWidth;
        }
        Integer beforeIndex = columnIndex - 1;
        divisor = pushBefore(row, divisor, beforeIndex, cellValue);
        columnWidth = (int) Math.ceil(columnWidth / divisor);
        if (divisor != 1) {
            for (int i = columnIndex - divisor + 1; i < columnIndex; i ++) {
                this.setColumnWidth(writeSheetHolder, columnWidth, row.getCell(i), maxColumnWidthMap, true);
            }
        }
        return columnWidth;
    }

    private Integer pushBefore(Row row, Integer divisor, Integer beforeIndex, String cellValue) {
        Cell afterCell = row.getCell(beforeIndex);
        beforeIndex --;
        if ((cellValue == null && (afterCell == null || afterCell.getStringCellValue() == null)) || cellValue.equals(afterCell.getStringCellValue())) {
            divisor ++;
            if (beforeIndex >= 0) {
                return pushBefore(row, divisor, beforeIndex, afterCell.getStringCellValue());
            }
        }
        return divisor;
    }

    @SuppressWarnings("rawtypes")
    private Integer dataLength(List<CellData> cellDataList, Cell cell, Boolean isHead) {
        if (isHead) {
            return cell.getStringCellValue().getBytes().length;
        } else {
            CellData cellData = cellDataList.get(0);
            CellDataTypeEnum type = cellData.getType();
            if (type == null) {
                return -1;
            } else {
                switch (type) {
                    case STRING:
                        return cellData.getStringValue().getBytes().length;
                    case BOOLEAN:
                        return cellData.getBooleanValue().toString().getBytes().length;
                    case NUMBER:
                        return cellData.getNumberValue().toString().getBytes().length;
                    default:
                        return -1;
                }
            }
        }
    }

}
